#include "wx/wxprec.h"
#if wxUSE_WCHAR_T

#include "wx/convauto.h"

wxConvAuto::BOMType wxConvAuto::DetectBOM( const char *src, size_t srcLen ) {
  if( srcLen < 2 ) {
    return BOM_None;
  }
  switch( *src++ ) {
    case '\0':
      if( srcLen >= 4 &&
          src[0] == '\0' &&
          src[1] == '\xfe' &&
          src[2] == '\xff' ) {
        return BOM_UTF32BE;
      }
      break;
    case '\xfe':
      if( *src++ == '\xff' ) {
        return BOM_UTF16BE;
      }
      break;
    case '\xff':
      if( *src++ == '\xfe' ) {
        return srcLen >= 4 && src[0] == '\0' && src[1] == '\0'
               ? BOM_UTF32LE
               : BOM_UTF16LE;
      }
      break;
    case '\xef':
      if( srcLen >= 3 && src[0] == '\xbb' && src[1] == '\xbf' ) {
        return BOM_UTF8;
      }
      break;
  }
  return BOM_None;
}

void wxConvAuto::InitFromBOM( BOMType bomType ) {
  m_consumedBOM = false;
  switch( bomType ) {
    case BOM_UTF32BE:
      m_conv = new wxMBConvUTF32BE;
      m_ownsConv = true;
      break;
    case BOM_UTF32LE:
      m_conv = new wxMBConvUTF32LE;
      m_ownsConv = true;
      break;
    case BOM_UTF16BE:
      m_conv = new wxMBConvUTF16BE;
      m_ownsConv = true;
      break;
    case BOM_UTF16LE:
      m_conv = new wxMBConvUTF16LE;
      m_ownsConv = true;
      break;
    case BOM_UTF8:
      m_conv = &wxConvUTF8;
      m_ownsConv = false;
      break;
    default:
      wxFAIL_MSG( _T( "unexpected BOM type" ) );
    case BOM_None:
      InitWithDefault();
      m_consumedBOM = true;
  }
}

void wxConvAuto::SkipBOM( const char **src, size_t *len ) const {
  int ofs;
  switch( m_bomType ) {
    case BOM_UTF32BE:
    case BOM_UTF32LE:
      ofs = 4;
      break;
    case BOM_UTF16BE:
    case BOM_UTF16LE:
      ofs = 2;
      break;
    case BOM_UTF8:
      ofs = 3;
      break;
    default:
      wxFAIL_MSG( _T( "unexpected BOM type" ) );
    case BOM_None:
      ofs = 0;
  }
  *src += ofs;
  if( *len != ( size_t ) - 1 ) {
    *len -= ofs;
  }
}

void wxConvAuto::InitFromInput( const char **src, size_t *len ) {
  m_bomType = DetectBOM( *src, *len );
  InitFromBOM( m_bomType );
  SkipBOM( src, len );
}

size_t
wxConvAuto::ToWChar( wchar_t *dst, size_t dstLen,
                     const char *src, size_t srcLen ) const {
  wxConvAuto *self = wx_const_cast( wxConvAuto *, this );
  if( !m_conv ) {
    self->InitFromInput( &src, &srcLen );
    if( dst ) {
      self->m_consumedBOM = true;
    }
  }
  if( !m_consumedBOM && dst ) {
    self->m_consumedBOM = true;
    SkipBOM( &src, &srcLen );
  }
  return m_conv->ToWChar( dst, dstLen, src, srcLen );
}

size_t
wxConvAuto::FromWChar( char *dst, size_t dstLen,
                       const wchar_t *src, size_t srcLen ) const {
  if( !m_conv ) {
    wx_const_cast( wxConvAuto *, this )->InitWithDefault();
  }
  return m_conv->FromWChar( dst, dstLen, src, srcLen );
}

#endif
